/*
 * @copyright 2011 Gerald Jacobson
 * @license GNU General Public License
 * 
 * This file is part of GameTimer.
 *
 * GameTimer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * GameTimer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with GameTimer. If not, see <http://www.gnu.org/licenses/>.
 */
package org.amphiption.gametimer.service;

import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import org.amphiption.gametimer.entity.Player;
import org.amphiption.gametimer.entity.TimerConfiguration;
import org.amphiption.gametimer.entity.TimerConfiguration.TimerType;

import android.app.Service;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.IBinder;

/**
 * @author Amphiprion
 * 
 */
public class TimerService extends Service implements ITimerService {
	private boolean running;
	private Timer timer;
	private TimerServiceBinder binder;
	private TimerServiceListener listener;
	private long last;
	private TimerState state;
	private TimerConfiguration configuration;
	private Player player;
	private Sensor accelerometerSensor;
	private SensorManager sensorManager;
	private boolean pausedBySensor;
	private int warnBeforeEndInSecond;

	@Override
	public void onCreate() {
		running = false;
		super.onCreate();
		state = TimerState.INIT;
		binder = new TimerServiceBinder(this);
	}

	@Override
	public void onStart(Intent intent, int startId) {
		// Log.d(ApplicationConstants.PACKAGE, "OnStart");
		running = true;
		configuration = (TimerConfiguration) intent.getSerializableExtra("TIMER_CONFIGURATION");

		sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
		List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
		if (sensorList.size() > 0) {
			accelerometerSensor = sensorList.get(0);
			sensorManager.registerListener(accelerometerListener, accelerometerSensor, SensorManager.SENSOR_DELAY_NORMAL);
		} else {
			accelerometerSensor = null;
		}
		for (Player p : configuration.getPlayers()) {
			if (configuration.getTimerType() == TimerType.GAME_TIMER) {
				if (!configuration.isHandicap()) {
					p.setCounter(configuration.getDuration() * 1000);
				} else {
					p.setCounter(p.getDuration() * 1000);
				}
			} else if (configuration.getTimerType() == TimerType.SHARED_GAME_TIMER) {
				p.setCounter(configuration.getDuration() * 1000);
			} else {
				p.setCounter(0);
			}
		}
		state = TimerState.SELECT_NEXT_PLAYER;
		listener.stateChanged();

	}

	private SensorEventListener accelerometerListener = new SensorEventListener() {

		@Override
		public void onAccuracyChanged(Sensor arg0, int arg1) {
			// TODO Auto-generated method stub

		}

		@Override
		public void onSensorChanged(SensorEvent arg0) {
			float z_value = arg0.values[2];
			if (z_value >= 0) {
				// restart
				if (pausedBySensor && !running) {
					pausedBySensor = false;
					pause();
				}
			} else {
				// pause
				if (running) {
					pausedBySensor = true;
					pause();
				}
			}
		}
	};

	@Override
	public void startPlayer(int index) {

		player = configuration.getPlayers().get(index);
		boolean startThread = true;
		switch (configuration.getTimerType()) {
		case TURN_TIMER:
			if (!configuration.isHandicap()) {
				player.setCounter(configuration.getDuration() * 1000);
			} else {
				player.setCounter(player.getDuration() * 1000);
			}
			break;

		case TURN_TIMER_SAVE:
			int newTime = 0;
			if (!configuration.isHandicap()) {
				newTime = configuration.getDuration() * 1000;
			} else {
				newTime = player.getDuration() * 1000;
			}
			if (configuration.getReportLimit() > 0) {
				newTime += Math.min(configuration.getReportLimit() * 1000, player.getCounter());
			} else {
				newTime += player.getCounter();
			}
			player.setCounter(newTime);
			break;
		case TURN_COUNTER:
			player.setCounter(player.getCounter() + 1);
			startThread = false;
			break;
		case GAME_TIMER:
			break;
		case SHARED_GAME_TIMER:
			break;
		case UPCOUNT:
			break;
		}
		listener.updateUi(player.getCounter());
		last = System.currentTimeMillis();

		if (startThread) {
			warnBeforeEndInSecond = configuration.getBeforeWarning();
			while (warnBeforeEndInSecond > 0 && warnBeforeEndInSecond > player.getCounter() / 1000) {
				warnBeforeEndInSecond -= configuration.getEveryWarning();
			}
			if (timer != null) {
				timer.cancel();
				timer = null;
			}
			timer = new Timer();
			timer.scheduleAtFixedRate(new TimerTask() {
				@Override
				public void run() {
					long now = System.currentTimeMillis();
					int elapsed = (int) (now - last);
					last = now;
					if (running) {
						if (configuration.getTimerType() == TimerType.UPCOUNT) {
							player.setCounter(player.getCounter() + elapsed);
						} else {
							if (player.getCounter() == 0) {
								return;
							}
							if (configuration.getTimerType() == TimerType.SHARED_GAME_TIMER) {
								int newCount = Math.max(0, player.getCounter() - elapsed);
								for (Player p : configuration.getPlayers()) {
									p.setCounter(newCount);
								}
							} else {
								player.setCounter(Math.max(0, player.getCounter() - elapsed));
							}
						}
						listener.updateUi(player.getCounter());
						if (player.getCounter() == 0) {
							if (timer != null) {
								timer.cancel();
								timer = null;
							}
							listener.endReached();
							state = TimerState.SELECT_NEXT_PLAYER;
							listener.stateChanged();
						} else if (configuration.getTimerType() != TimerType.UPCOUNT) {
							if (warnBeforeEndInSecond >= player.getCounter() / 1000.0f) {
								listener.warn();
								warnBeforeEndInSecond -= configuration.getEveryWarning();
							}
						}
					}
				}
			}, 0, 1000);
		}
	}

	@Override
	public void pause() {
		running = !running;
		if (running) {
			listener.resumed();
		} else {
			listener.paused();
		}
	}

	@Override
	public void onDestroy() {
		running = false;
		if (accelerometerListener != null) {
			sensorManager.unregisterListener(accelerometerListener);
			accelerometerListener = null;
		}
		if (timer != null) {
			timer.cancel();
			timer = null;
		}
	}

	@Override
	public void stop() {
		state = TimerState.INIT;
		running = false;
		if (timer != null) {
			timer.cancel();
			timer = null;
		}
		this.stopSelf();
	}

	@Override
	public void changePlayerAsked() {
		if (timer != null) {
			timer.cancel();
			timer = null;
		}
		state = TimerState.SELECT_NEXT_PLAYER;
		listener.stateChanged();
	}

	@Override
	public IBinder onBind(Intent intent) {
		return binder;
	}

	@Override
	public void setTimerServiceListener(TimerServiceListener listener) {
		this.listener = listener;
	}

	@Override
	public boolean isRunning() {
		return running;
	}

	@Override
	public TimerState getState() {
		return state;
	}

	@Override
	public TimerType getType() {
		return configuration.getTimerType();
	}

	public interface TimerServiceListener {
		void updateUi(int counter);

		void endReached();

		void stateChanged();

		void paused();

		void resumed();

		void warn();
	}
}